#ifndef _JSON_H_
#define _JSON_H_

#include <vector>
#include <string>
#include <map>

#include "zasciiz.h"

/** \brief Класс для хранения пар ключ->значение полученных при разборе JSON-объекта.
  *
  * Каждый JSON-объект содержит последовательность из одного и более пар ключ->значение
  * разделенных запятыми. В объекте данного класса парсер накапливает пары по одному
  * текущему JSON-объекту. По завершении парсинга очередного JSON-объекта, объект
  * данного класса раздается слушателям на обработку, после чего очищается от прежних
  * пар и становится готовым к накоплению пар со следующего JSON-объекта.
  */
class JsonValuesMap
{
public:
    //! \brief Очистка внутренних контейнеров от накопленных пар ключ->значение
    void clear();
    //! \brief Добавить пару с целым значением
    void addIntValue(const char *pchKey, int iValue);
    //! \brief Добавить пару со строковым значением
    void addStrValue(const char *pchKey, const char *pchValue);

    //! \brief Проверить наличие пары со строковым значением по заданному ключу
    bool isStrValue(const char *pchKey);
    //! \brief Проверить наличие пары со строковым значением по заданному ключу
    bool isStrValue(std::string sKey);
    //! \brief Проверить наличие пары с целым значением по заданному ключу
    bool isIntValue(const char *pchKey);
    //! \brief Проверить наличие пары с целым значением по заданному ключу
    bool isIntValue(std::string sKey);

    //! \brief Получить строковое значение по заданному ключу
    std::string getStrValue(const char *pchKey);
    //! \brief Получить строковое значение по заданному ключу
    std::string getStrValue(std::string sKey);
    //! \brief Получить целое значение по заданному ключу
    int getIntValue(const char *pchKey);
    //! \brief Получить целое значение по заданному ключу
    int getIntValue(std::string sKey);
private:
    //! \brief Контейнер для хранения пар с целыми значениями
    std::map<std::string, int> m_mpIntValue;
    //! \brief Контейнер для хранения пар со строковыми значениями
    std::map<std::string, std::string> m_mpStrValue;
};

/** \brief Абстрактный класс для слушателей результатов парсинга JSON-объекта
  *
  * Создаете подкласс данного класса с определением виртуальных методов
  * вызываемых для передачи данных по очередному извлеченому JSON-объекту
  * и для передачи информации об ошибке парсинга текущего JSON-объекта.
  */
class JsonListener
{
public:
    //! \brief Вызывается для передачи параметров по извлеченному JSON-объекту
    virtual void readyRead(JsonValuesMap *) = 0;
    //! \brief Вызывается для передачи информации об ошибке по текущему JSON-объекту
    virtual void parseError(std::string sError) = 0;
};

/** \brief Класс вспомогательного парсера, используемого для извлечения
  * строковых значений.
  *
  * Содежит конечный автомат (КА) с набором состояний для извлечения
  * изображения строк.
  */
class StringParser
{
public:
    StringParser();

    //! \brief Сброс конечного автомата парсера
    void reset();
    //! \brief Передача в КА пробельного символа (выделено для оптимизации)
    void appendSpace(char ch);
    //! \brief Передача в КА непробельного символа
    void append(char ch);
    //! \brief Проверка успешного окончания парсинга
    bool isOk() { return m_state == PS_STOP; }
    //! \brief Проверка текущего режима КА на состояние извлечения значения
    bool isValueMode() { return m_state == PS_VALUE; }

    //! \brief Получить строковое значение, извлеченное при успешном завершении КА
    const char* getValue() { return m_zValue.data(); }
    //! \brief Получить строковое описание ошибки обнаруженной при работе КА
    const char* getLastError() { return m_zLastError.data(); }

private:
    //! \brief Список состояний конечного автомата
    typedef enum {PS_START, PS_VALUE, PS_STOP, PS_ERROR} ParserState_t;
    //! \brief Текущее состояние конечного автомата
    ParserState_t m_state;

    //! \brief Флаг маскирования символа. Устанавливается при чтение символа '\'
    bool bMaskMode;
    AsciiZ m_zValue;
    AsciiZ m_zLastError;
};

/** \brief Класс вспомогательного парсера, используемого для извлечения
  * целых значений.
  *
  * Содежит конечный автомат (КА) с набором состояний для извлечения
  * изображения целых чисел.
  */
class IntParser
{
public:
    IntParser();

    //! \brief Сброс конечного автомата парсера
    void reset();
    //! \brief Передача в КА пробельного символа (выделено для оптимизации)
    void appendSpace();
    //! \brief Передача в КА непробельного символа
    void append(char ch);
    //! \brief Проверка успешного окончания парсинга
    bool isOk() { return m_state != PS_ERROR; }

    //! \brief Получить целое значение, извлеченное при успешном завершении КА
    int getValue() { return m_iValue; }
    //! \brief Получить строковое описание ошибки обнаруженной при работе КА
    const char* getLastError() { return m_zLastError.data(); }

private:
    //! \brief Список состояний конечного автомата
    typedef enum {PS_START, PS_VALUE, PS_STOP, PS_ERROR} ParserState_t;
    //! \brief Текущее состояние конечного автомата
    ParserState_t m_state;

    int m_iValue;
    AsciiZ m_zLastError;
};

/** \brief Простой потоковый парсер JSON-объектов
  *
  * Парсер JSON-объектов. Работает в потоковом режиме, т.е. способен принимать
  * последовательные порции данных содержащих последовательности JSON-объектов.
  * По мере накопления параметров JSON-объекта, сохраняет их в объекте класса
  * JsonValuesMap. При извлечении очередного JSON-объекта целиком сообщает об
  * этом через слушателей (объекты класса JsonListener), которые были переданы
  * в парсер.
  *
  * Чтобы использовать парсер, надо создать подклассы слушателей (наследники
  * JsonListener), зарегистрировать их в парсере через метод addListener() и
  * просто передавать все принимаемые данные с последовательностями JSON-объектов
  * в метод parse().
  *
  * Парсер понимает ограниченное подмножество записей спецификации JSON.
  *
  * - Только целые и строковые параметры.
  * - Не понимает вложенных JSON-объектов.
  *
  * @sa JsonValuesMap, JsonListener, IntParser, StringParser
  *
  * @todo Реализовать поддержку UTF-8. Сейчас парсер понимает только
  * однобайтовые коды символов.
  */
class JsonParser
{
public:
    JsonParser();
    ~JsonParser();

    //! \brief Регистратор объектов слушателей.
    void addListener(JsonListener *);

    //! \brief Сброс внутренннего состояния парсера
    void reset();
    //! \brief Парсер входного потока данных
    void parse(const char *data, int len);

private:
    char m_ucNL; // флаг переноса (для правильного учета кодов переноса строк с разных платформ)
    int  m_iRow; // счетчик строк (обнуляется в начале каждого JSON-объекта)
    int  m_iCol; // счетчик столбцов (позиция символа в строке)

    //! \brief Список слушателей
    std::vector<JsonListener*> m_vListener;

    //! \brief Тип-нумератор возможных внутренних состояний конечного автомата парсера
    typedef enum {PS_START, PS_KEY, PS_VALUE, PS_INT_VALUE, PS_STR_VALUE,
                  PS_ERROR} ParserState_t;

    //! \brief Текущее состояние конечного автомата парсера
    ParserState_t m_state;

    //! \brief Строка с состоянием о последней ошибке. Отдается слушателям.
    AsciiZ m_zLastError;
    //! \brief Вспомогательный объект парсера строковых значений в JSON-объекте
    StringParser m_strParser;
    //! \brief Вспомогательный объект парсера целых значений в JSON-объекте
    IntParser m_intParser;
    //! \brief Значение последнего извлеченного ключа (имени параметра)
    AsciiZ m_zCurrentKey;
    //! \brief Объект для накопления параметров (ключ->значение) из текущего JSON-объекта
    JsonValuesMap m_jsonValue;

    //! \brief Цикл передачи извлеченного JSON-объекта слушателям
    void readyRead();
};

#endif

